#ifndef INCLUDE_GUARD_MX5420WZUBP3RFALH768TSOY9JEND1GICK
#define INCLUDE_GUARD_MX5420WZUBP3RFALH768TSOY9JEND1GICK

/*
 * norx_bits.hpp
 * This file is part of norxx
 *
 * Copyright (C) 2014 - Sebastian Gesemann
 *
 * norxx is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * norxx is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with norxx. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>
#include <cstdint>
#include <climits>
#include <type_traits>

#ifndef UCHAR_MAX
	#error "UCHAR_MAX not defined in <climits>?!"
#endif

#ifndef NORX_USE_MEMCPY
	#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
	    (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && (UCHAR_MAX == 255)
		#define NORX_USE_MEMCPY 1
	#else
		#define NORX_USE_MEMCPY 0
	#endif
#endif

#if NORX_USE_MEMCPY
	#include <cstring> // for std::memcpy
#endif

#include "array_ref.hpp"
#include "integers.hpp"

namespace norx {

using util::u8;
using util::u32;
using util::u64;
using util::bits;
using util::int_type;
using util::array_ref;

template<int W>
struct consume_words
{
	template<class T>
	inline array_ref<T> operator()(array_ref<T>& ar, unsigned num) const
	{
		return ar.consume_first_n((W/bits<T>::value)*num);
	}
};

template<class T>
void bytes2word(array_ref<const u8> in, T& out)
{
	const int octets_per_word = bits<T>::value / 8;
	assert(in.size() >= sizeof(out));
	u32 tmp = 0;
	for (int k=0; k<octets_per_word; ++k) {
		tmp |= T(in[k] & 0xFFu) << (8*k);
	}
	out = tmp;
}

template<class T>
void word2bytes(T const in, array_ref<u8> out)
{
	const int octets_per_word = bits<T>::value / 8;
	assert(out.size() >= sizeof(in));
	for (int k=0; k<octets_per_word; ++k) {
		out[k] = (in >> (8*k)) & 0xFFu;
	}
}

template<class T>
void bytes2words_generic(array_ref<const u8> b, array_ref<T> w)
{
	const unsigned octets_per_word = bits<T>::value / 8;
	assert(w.size()*octets_per_word == b.size());
	#if NORX_USE_MEMCPY
		std::memcpy(w.base(),b.base(),w.size()*sizeof(T));
	#else
		for (unsigned idx=0, end=w.size(); idx<end; ++idx) {
			bytes2word(b.consume_first_n(octets_per_word),w[idx]);
		}
	#endif
}

template<class T>
void words2bytes_generic(array_ref<const T> w, array_ref<u8> b)
{
	const unsigned octets_per_word = bits<T>::value / 8;
	assert(w.size()*octets_per_word == b.size());
	#if NORX_USE_MEMCPY
		std::memcpy(b.base(),w.base(),b.size());
	#else
		for (unsigned idx=0, end=b.size()/octets_per_word; idx<end; ++idx) {
			word2bytes(w[idx],b.consume_first_n(octets_per_word));
		}
	#endif
}

template<class T, class U
	,class = typename std::enable_if<
		sizeof(T)==sizeof(U) && std::is_convertible<T,U>::value
	>::type
>
inline void copy(array_ref<T> from, array_ref<U> to)
{
	assert(from.size() == to.size());
	std::copy(from.begin(),from.end(),to.begin());
}

inline void copy(array_ref<const u8> from, array_ref<u32> to)
{ bytes2words_generic(from,to); }

inline void copy(array_ref<const u8> from, array_ref<u64> to)
{ bytes2words_generic(from,to); }

inline void copy(array_ref<const u32> from, array_ref<u8> to)
{ words2bytes_generic(from,to); }

inline void copy(array_ref<const u64> from, array_ref<u8> to)
{ words2bytes_generic(from,to); }

bool match(array_ref<const u8> x, array_ref<const u8> y);

} // namespace norx

#endif

